#! /usr/bin/env python

# -*- coding: UTF-8 -*-
# ==============================================================================
# Filename : SshEdit.py
# Purpose  :
# Author   : Yohann Li (lyh001686@sina.com)
# Date     : 2020-07-23 13:55
# ==============================================================================
import sys

from PyQt5.QtCore import QObject, QEvent, Qt, pyqtSignal
from PyQt5.QtGui import QKeySequence
from PyQt5.QtWidgets import QApplication, QShortcut

from Widgets.TextEdit import TextEdit
from conf.configuration import logger
from script.tcprelay import SSH


class SSHEdit(TextEdit):
    close_signal = pyqtSignal(str)

    def __init__(self, *args, **kwargs):
        super(SSHEdit, self).__init__(*args, **kwargs)
        self.setObjectName('sshedit')
        self.setWindowModality(Qt.ApplicationModal)
        # self.edit = None
        # self._init_ui()
        self.mode = 'OS'
        self.port_offset = kwargs['port_offset']
        self.ssh = SSH(self.port_offset, self)
        self._setup_signal()
        self._setup_shortcut()
        self.ssh.start()
        self.installEventFilter(self)
        self.resize(900, 600)

    def new_line(self):
        self.appendPlainText(self.prefix.get(self.mode))

    def _setup_shortcut(self):
        QShortcut(QKeySequence.Close, self).activated.connect(self.close)
        QShortcut(QKeySequence('Ctrl+K'), self).activated.connect(self.clear)
        QShortcut(QKeySequence.SaveAs, self).activated.connect(self.save_as)
        QShortcut(QKeySequence.Save, self).activated.connect(self.save)

    def _setup_signal(self):
        self.ssh.output_signal.connect(self.recv_)
        self.key_pressed_signal.connect(self.ssh.execute)
        self.tab_pressed_signal.connect(self.ssh.execute)
        self.fun_pressed_signal.connect(self.ssh.execute)

    def resizeEvent(self, a0) -> None:
        # self.edit.resize(self.size())
        super(SSHEdit, self).resizeEvent(a0)

    def closeEvent(self, a0) -> None:
        self.close_signal.emit(self.objectName())
        self.ssh.quit()
        self.close()

    def eventFilter(self, a0: QObject, a1: QEvent) -> bool:
        if a1.type() == QEvent.KeyPress:
            """return事件不传递到编辑器"""
            self.pass_key_event_signal.emit(a1)  # 发送信号给Minimap的keyPressEvent
            if a1.key() == Qt.Key_Tab:  # tab
                """1. 远端没有补充cmd,且无输出
                    ~ root# L\t -> ~ root# L

                2. 远端没有补充cmd,但有cmd选项输出(需要按下两次tab)
                    ~ root# L\t\t -> 
                    ~ root# L
LCDTest                          LaunchMarathon                   ListLoad
                         ListLoadCmd                    ListLoadCmd_DTrace-Automated.sh
                3. 远端补充了部分cmd
                4. 远端补全了cmd
                    ~ root# Lib\t -> ~ root# Library"""
                logger.debug(f"cmd long: {self.cmd_long}")
                key = '\t'
                # for _ in range(len(self.cmd_long)):
                #     self.textCursor().deletePreviousChar()
                # self.key_pressed_signal.emit(key)
                # self.tab_pressed_signal.emit(self.cmd_long + key)  # printable key 储存等待\n或\t发送的情况
                # 例如: rmt = local = 'Library', 然后将cursor移到行首，输入ls， 再将光标移到行末，
                # 此时按下return键，rmt = 'Library ls\n', local = 'ls Library\n'
                # 遂此种方法不可取
                # ______________________________________________________
                self.tab_pressed_signal.emit(key)  # printable key 随时发送的情况
                self.pressed_key(key='tab')
                return True
            elif a1.key() == Qt.Key_Up:  # arrow up  ^[[A
                if self.cmd_history.get(self.mode, ''):
                    try:
                        self._tracker -= 1
                        last_cmd = self.cmd_history.get(self.mode)[self._tracker]
                    except IndexError:
                        self._tracker = -1
                        last_cmd = self.cmd_history.get(self.mode)[self._tracker]
                    self.fun_pressed_signal.emit('\x15') if self.rmt_cmd else ''  # 如果此时远端有stdin,则删除
                    self.cmd_long = self.rmt_cmd = last_cmd
                    # 重写当前行的前缀
                    self.reset_cursor_pos()
                    self.reset_cur_line()
                    # 插入上一条命令
                    # self.insert(last_cmd)
                    self.key_pressed_signal.emit(self.cmd_long)
                return True
            elif a1.key() == Qt.Key_Down:  # arrow down  ^[[B
                if self.cmd_history.get(self.mode, ''):
                    try:
                        self._tracker += 1
                        last_cmd = self.cmd_history.get(self.mode)[self._tracker]
                    except IndexError:
                        self._tracker = 0
                        last_cmd = self.cmd_history.get(self.mode)[self._tracker]
                    self.fun_pressed_signal.emit('\x15') if self.rmt_cmd else ''  # 如果此时远端有stdin,则删除
                    self.cmd_long = self.rmt_cmd = last_cmd
                    self.reset_cursor_pos()
                    self.reset_cur_line()
                    # self.insert(last_cmd)
                    self.key_pressed_signal.emit(self.cmd_long)
                return True
            elif a1.key() == Qt.Key_Left:  # arrow left  ^[[D
                self.pressed_key(key="left_arr")
                pos_in_block = self.textCursor().positionInBlock  # 获取光标位置
                prefix_len = len(self.prefix.get(self.mode))
                logger.debug(f"prefix: {repr(self.prefix.get(self.mode))} prefix_len: {prefix_len} pos_in_block: {pos_in_block()}")
                if prefix_len >= pos_in_block() and \
                        self.textCursor().block().text().__contains__(self.prefix.get(self.mode, '')):
                    return True
                self.fun_pressed_signal.emit('\033[D')
                if a1.modifiers() & Qt.AltModifier:
                    logger.debug(f"Alt + Left disabled")
                    return True
                    # cur = self.textCursor()
                    # cur.movePosition(QTextCursor.PreviousWord, QTextCursor.MoveAnchor)
                    # self.setTextCursor(cur)
                    # # self.fun_pressed_signal.emit("\x12\x1b[D")
                    # self.fun_pressed_signal.emit("\x1b[1;3D")
                self.cur_pos = pos_in_block() - prefix_len  # 这个当前位置是光标移动前的位置
                # self.cur_pos = pos_in_block()  # 这个当前位置是光标移动前的位置
                logger.debug(f"cur cursor pos: {self.cur_pos}")
                # return True
            elif a1.key() == Qt.Key_Right:  # arrow right  ^[[C
                self.pressed_key(key='right_arr')
                self.fun_pressed_signal.emit('\x1b[C')
                self.cur_pos += 1
                if a1.modifiers() & Qt.AltModifier:
                    logger.debug(f"Alt + Right disabled")
                    return True
            elif a1.matches(QKeySequence.Paste):
                logger.debug("allocate paste")
                key = QApplication.clipboard().text()
                self.cmd_long += key
                self.rmt_cmd += key
                self.reset_cursor_pos()
                self.key_pressed_signal.emit(f"{key}")
                self.paste_timer.start(self.read_delay * 2)
                return True
            # elif a1.key() == Qt.Key_Control:
            #     logger.debug(f"pressed ctrl")
            #     return True
            # elif a1.key() == Qt.Key_Meta:
            #     logger.debug(f"pressed meta")
            elif a1.key() == Qt.Key_Escape:
                logger.debug(f"Pressed Esc")
                self.fun_pressed_signal.emit('\x1B')

        return super(TextEdit, self).eventFilter(a0, a1)

    # def keyPressEvent(self, e) -> None:
    #     # if e.text() in string.printable:
    #     #     self.pass_key_event_signal.emit(e.text())
    #     #     return
    #     # elif e.key() == Qt.Key_Tab
    #     ctrlOrShift = e.modifiers() & (Qt.ControlModifier | Qt.ShiftModifier)
    #     hasModifier = (e.modifiers() != Qt.NoModifier) and not ctrlOrShift  # 非Ctrl/Shift的控制键
    #     # 按下非Ctrl/Shift/Printable Key，不显示completer
    #     # logger.debug(f"key not shift, meta {e.modifiers() not in (Qt.ShiftModifier, Qt.MetaModifier)}")
    #     logger.debug(f"hasModifier: {repr(hasModifier)}, key: {repr(e.text())}, "
    #                  f"len: {repr(len(e.text()))}, keyID: {repr(e.key())}")
    #     if (hasModifier or len(e.text()) == 0) and e.modifiers() not in (Qt.ShiftModifier, Qt.MetaModifier):
    #         # if (hasModifier or len(e.text())==0) and e.key() > 250:
    #         logger.debug(f"e.text len = 0; {repr(e.text())}")
    #         self.hide_completer(744)
    #         return super(TextEdit, self).keyPressEvent(e)  # 这个return会阻挡快捷键，例如Ctrl+A, 去掉条件 len(e.text()) == 0 后暂时可以用
    #         # return
    #     # Completer可视情况下，按Tab 选择item
    #     if self._completer.popup().isVisible() and e.key() in (Qt.Key_Tab,):  # 取消使用回车键选择item的功能
    #         # logger.debug(f"")
    #         e.ignore()
    #         return
    #
    #     key = e.text()
    #
    #     if e.text() and e.text() in string.digits + string.ascii_letters + string.punctuation + ' ':
    #         logger.debug(f"Pressed key {repr(key)} {e.key()}")
    #         self.resume_echo_status()
    #         self._tracker = 0  # reset tracker to 0
    #         self.reset_cursor_pos()
    #
    #         if self.cmd_long and self.cmd_long.startswith(self.rmt_cmd) and self.rmt_cmd != self.cmd_long:
    #             # 如果local为真且local真包含rmt
    #             _index = self.cmd_long.index(self.rmt_cmd) + len(self.rmt_cmd)
    #             _key = self.cmd_long[_index:] + key
    #             self.reset_cur_line()
    #             self.key_pressed_signal.emit(_key)
    #             logger.debug(f"send key: {_key}")
    #             self.rmt_cmd += self.cmd_long[_index:]
    #         else:
    #             self.key_pressed_signal.emit(key)
    #             logger.debug(f"send key: {key}")
    #
    #         if self.textCursor().atEnd():
    #             self.rmt_cmd += key
    #             self.cmd_long += key
    #         else:
    #             cur_pos = self.cur_pos_in_cmd()
    #             self.rmt_cmd = self.rmt_cmd[:cur_pos] + key + self.rmt_cmd[cur_pos:]
    #             self.cmd_long = self.cmd_long[:cur_pos] + key + self.cmd_long[cur_pos:]
    #         # return True
    #     elif e.key() == Qt.Key_Return:  # return
    #         key = '\n'
    #         cmd = self.cmd_long
    #         self.pressed_key('return')
    #         if self.rmt_cmd:
    #             self.key_pressed_signal.emit(key)
    #         else:
    #             for _ in range(len(cmd)):
    #                 self.textCursor().deletePreviousChar()
    #             self.key_pressed_signal.emit(self.cmd_long + key)
    #         self.cmd_long = ''
    #         self.rmt_cmd = ''
    #         self.cursor_pre_pos = None
    #         # self.new_line()
    #         # self.reset_cursor_pos()
    #         self.hide_completer(797)
    #         return
    #     elif e.key() == Qt.Key_Backspace:  # backspace
    #         """1. 在远端stdin存在cmd的时候，按下\\b要传过去"""
    #         self.pressed_key("bac")
    #         self.fun_pressed_signal.emit('\x08')  # 发送退格键到shell
    #         self.hide_completer(803)
    #         prefix_len = len(self.prefix.get(self.mode, ''))  # 获取当前模式要保留的前缀长度
    #         logger.info(
    #             f"Remote cmd: {repr(self.rmt_cmd)}, cmd : {repr(self.cmd_long)}") if self.rmt_cmd else ''
    #         # 如果前缀长度小于段落长度则可以删除字符，否则不能删除
    #         if prefix_len >= self.textCursor().positionInBlock() and \
    #                 self.textCursor().block().text().__contains__(self.prefix.get(self.mode, '')):
    #             logger.debug(f"mode {self.mode}, text {repr(self.textCursor().block().text())}, "
    #                          f"prefix {repr(self.prefix.get(self.mode))}")
    #             logger.debug(
    #                 f"Prefix length:{len(self.prefix.get(self.mode))} pos:{self.textCursor().positionInBlock()}")
    #             return  # 拦截事件
    #         else:
    #             cur_pos = self.cur_pos_in_cmd()
    #             self.rmt_cmd = self.rmt_cmd[:cur_pos - 1] + self.rmt_cmd[cur_pos:]
    #             self.cmd_long = self.cmd_long[:cur_pos - 1] + self.cmd_long[cur_pos:]
    #         logger.info(f"Remote cmd: {repr(self.rmt_cmd)}") if self.rmt_cmd else ''
    #     elif e.key() == Qt.Key_Delete:
    #         logger.debug(f"Pressed delete")
    #         cur_pos = self.textCursor().positionInBlock() - len(self.prefix.get(self.mode))
    #         assert cur_pos >= 0, f"cursor position: {cur_pos}"
    #         self.rmt_cmd = self.rmt_cmd[:cur_pos] + self.rmt_cmd[cur_pos + 1:]
    #         self.cmd_long = self.rmt_cmd
    #         self.fun_pressed_signal.emit('\x1b[C')
    #         self.fun_pressed_signal.emit('\x08')
    #         self.pressed_key('del')
    #         self.hide_completer(828)
    #     elif e.key() == Qt.Key_A and e.modifiers() & Qt.MetaModifier:
    #         logger.debug(f"pressed ctrl+a")
    #         # pre_pos = self.textCursor().positionInBlock()
    #         prefix_len = len(self.prefix.get(self.mode))
    #         cur = self.textCursor()
    #         cur.setPosition(cur.block().position() + prefix_len, QTextCursor.MoveAnchor)
    #         self.setTextCursor(cur)
    #         # displacement = pre_pos - prefix_len  # 鼠标位移长度
    #         # [self.fun_pressed_signal.emit('\x1b[D') for _ in displacement]
    #         self.fun_pressed_signal.emit('\x01')
    #         return
    #     elif e.key() == Qt.Key_E and e.modifiers() & Qt.MetaModifier:
    #         logger.debug(f"pressed ctrl+e")
    #         self.fun_pressed_signal.emit('\x05')
    #         # return True
    #     elif e.key() == Qt.Key_U and e.modifiers() & Qt.MetaModifier:
    #         logger.debug(f"Pressed ctrl+U")
    #         self.fun_pressed_signal.emit('\x15')
    #         # 删除远端命令，删除本地当前行，并重写当前行的前缀
    #         self.reset_cur_line()
    #     elif e.key() == Qt.Key_C and e.modifiers() & Qt.MetaModifier:
    #         logger.debug(f"Send SIGNAL TERMINATE")
    #         self.fun_pressed_signal.emit('\x03')
    #         # return True
    #
    #     # 传递按键信号给minimap
    #     self.pass_key_event_signal.emit(e)
    #
    #     # 根据按键来设置补全功能的前缀 completer.completionPrefix
    #     if e.text() and e.text() in string.digits + string.ascii_letters + string.punctuation:
    #         # self.completion_prefix = self.word_under_cursor(e.text())
    #         self.completion_prefix = self.cmd_long
    #         self._completer.setCompletionPrefix(self.completion_prefix)
    #         self._completer.popup().setCurrentIndex(self._completer.completionModel().index(0, 0))
    #
    #         cr = self.cursorRect()
    #         cr.setWidth(self._completer.popup().sizeHintForColumn(0)
    #                     + self._completer.popup().verticalScrollBar().sizeHint().width())
    #         self._completer.complete(cr)
    #         return
    #     return super(TextEdit, self).keyPressEvent(e)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    app.setCursorFlashTime(0)
    t = SSHEdit()
    t.show()
    sys.exit(app.exec_())
